using System;
using Firestarter.Test.Base;
using FireStarterModeller.BO;
using Habanero.Base;
using Habanero.BO;
using NUnit.Framework;
using Rhino.Mocks;

namespace FireStarterModeller.Test.BO
{
    [TestFixture]
    public class TestDMRelationshipOneToOneOneSideMustBeAssociation
    {
        [SetUp]
        public void SetupTest()
        {
            //Runs every time that any testmethod is executed
            TestUtilsDomain.SetupTest();
            BORegistry.DataAccessor = new DataAccessorInMemory();
        }

        [TestFixtureSetUp]
        public void TestFixtureSetup()
        {
            //Code that is executed before any test is run in this class. If multiple tests
            // are executed then it will still only be called once.
            TestUtilsDomain.SetupFixture();
        }

        [TearDown]
        public void TearDownTest()
        {
            //runs every time any testmethod is complete
        }

        #region DMRelationshipOneToOneOneSideMustBeAssociation

        [Test]
        public void Test_CreateDMRelationshipManyOneRelationshipMustBeAssociationRule()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsDMClass.CreateUnsavedDefaultDMClass();
            DMRelationship relationship = dmClass.OwnerRelationships.CreateBusinessObject();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IBusinessObjectRule rule = new DMRelationshipOneToOneOneSideMustBeAssociation();
            //---------------Test Result -----------------------
            Assert.IsNotNull(rule);
            Assert.AreEqual("DMRelationshipOneToOneOneSideMustBeAssociation", rule.Name);
            Assert.AreEqual(ErrorLevel.Error, rule.ErrorLevel);
        }

//        [Test]
//        public void Test_CreateDMRelationshipManyOneRelationshipMustBeAssociationRule_WhenClassNull_ShouldRaiseError()
//        {
//            //---------------Set up test pack-------------------
//            IDMRelationship relationship = CreateRelationship();
//            //---------------Assert Precondition----------------
//            Assert.IsNull(relationship.OwnerClass);
//            //---------------Execute Test ----------------------
//            try
//            {
//                new DMRelationshipOneToOneOneSideMustBeAssociation(null);
//                Assert.Fail("expected ArgumentNullException");
//            }
//                //---------------Test Result -----------------------
//            catch (ArgumentNullException ex)
//            {
//                StringAssert.Contains("Value cannot be null", ex.Message);
//                StringAssert.Contains("dmRelationship", ex.ParamName);
//            }
//        }

        [Test]
        public void Test_IsValid_WhenOneToMany_BothComposition_ShouldRetTrue()
        {
            //Although this is an invalid relationship it is checked by another rule.
            //---------------Set up test pack-------------------
            IDMRelationship relationship = CreateRelationship();
            SetAsComposition(relationship);
            SetAsManyToOne(relationship);
            IDMRelationship reverseRelationship = relationship.ReverseRelationship;
            SetAsComposition(reverseRelationship);
            SetAsOneToMany(reverseRelationship);

            IBusinessObjectRule rule = new DMRelationshipOneToOneOneSideMustBeAssociation();
            //---------------Assert Precondition----------------
            AssertIsCompostion(relationship);
            AssertIsManyToOne(relationship);
            AssertIsCompostion(reverseRelationship);
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(relationship);
            //---------------Test Result -----------------------
            Assert.IsTrue(isValid, "Should be valid");
        }

        [Test]
        public void Test_IsValid_OneToOne_WhenBothHaveComposition_ShouldFail()
        {
            //---------------Set up test pack-------------------
            IDMRelationship relationship = CreateRelationship();
            SetAsComposition(relationship);
            SetAsOneToOne(relationship);
            IDMRelationship reverseRelationship = relationship.ReverseRelationship;
            SetAsComposition(reverseRelationship);
            SetAsOneToOne(reverseRelationship);

            IBusinessObjectRule rule = new DMRelationshipOneToOneOneSideMustBeAssociation();
            //---------------Assert Precondition----------------
            AssertIsCompostion(relationship);
            AssertIsOneToOne(relationship);
            AssertIsCompostion(reverseRelationship);
            AssertIsOneToOne(reverseRelationship);
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(relationship);
            //---------------Test Result -----------------------
            Assert.IsFalse(isValid);
            StringAssert.Contains("either the Relationship '", rule.Message);
            StringAssert.Contains("'" + relationship + "' on Class '" + relationship.ClassDM + "'", rule.Message);
            StringAssert.Contains("' or the reverse relationship '" + relationship.ReverseRelationship + "'", rule.Message);
            StringAssert.Contains("' on Class '" + relationship.RelatedClass + "' must be 'Association'", rule.Message);
        } 
        [Test]
        public void Test_IsValid_WhenReverseRelationshipNull_ShouldPass()
        {
            //Although this is invalid i.e. must always have a reverse rel
            // this must be detected by another rule.
            //---------------Set up test pack-------------------
            DMRelationship dmRelationship = new DMRelationship
                    {
                        RelationshipType = RelationshipType.Composition,
                        ReverseRelationship = null
                    };

            IBusinessObjectRule rule = new DMRelationshipOneToOneOneSideMustBeAssociation();
            //---------------Assert Precondition----------------
            Assert.IsNull(dmRelationship.ReverseRelationship);
            //---------------Execute Test ----------------------
            var isValid = rule.IsValid(dmRelationship);
            //---------------Test Result -----------------------
            Assert.IsTrue(isValid, "If there is no reverse rel it should always pass");
        } 
        
        [Test]
        public void Test_IsValid_WhenBothHaveAggregation_ShouldFail()
        {
            //---------------Set up test pack-------------------
            DMRelationship dmRelationship = new DMRelationship();
            dmRelationship.Cardinality = Cardinality.Single;
            dmRelationship.ReverseRelationship.Cardinality = Cardinality.Single;
            dmRelationship.RelationshipType = RelationshipType.Aggregation;
            dmRelationship.ReverseRelationship.RelationshipType = RelationshipType.Aggregation;
            IBusinessObjectRule rule = new DMRelationshipOneToOneOneSideMustBeAssociation();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(dmRelationship);
            
            //---------------Test Result -----------------------
            Assert.IsFalse(isValid);
            TestUtilsShared.AssertStringNotEmpty(rule.Message, "rule.Message");
        }       

        [Test]
        public void Test_IsValid_WhenHaveBothAggregationAndComposition_ShouldFail()
        {
            //---------------Set up test pack-------------------
            DMRelationship dmRelationship = new DMRelationship();
            IBusinessObjectRule rule = new DMRelationshipOneToOneOneSideMustBeAssociation();
            dmRelationship.Cardinality = Cardinality.Single;
            dmRelationship.ReverseRelationship.Cardinality = Cardinality.Single;
            dmRelationship.RelationshipType = RelationshipType.Aggregation;
            dmRelationship.ReverseRelationship.RelationshipType = RelationshipType.Composition;
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(dmRelationship);
            //---------------Test Result -----------------------
            Assert.IsFalse(isValid);
            TestUtilsShared.AssertStringNotEmpty(rule.Message, "rule.Message");
        }       
        [Test]
        public void Test_IsValid_WhenOneAssociation_ShouldPass()
        {
            //---------------Set up test pack-------------------
            DMRelationship dmRelationship = new DMRelationship();
            IBusinessObjectRule rule = new DMRelationshipOneToOneOneSideMustBeAssociation();
            dmRelationship.Cardinality = Cardinality.Single;
            dmRelationship.ReverseRelationship.Cardinality = Cardinality.Single;
            dmRelationship.RelationshipType = RelationshipType.Composition;
            dmRelationship.ReverseRelationship.RelationshipType = RelationshipType.Association;
            //---------------Assert Precondition----------------
            Assert.AreEqual(RelationshipType.Association, dmRelationship.ReverseRelationship.RelationshipType);
            //---------------Execute Test ----------------------
            bool isValid = rule.IsValid(dmRelationship);
            //---------------Test Result -----------------------
            Assert.IsTrue(isValid);
        }

        private static void AssertIsOneToMany(IDMRelationship relationship)
        {
            Assert.IsTrue(relationship.IsOneToMany, "Should be one to Many");
        }
        private static void AssertIsManyToOne(IDMRelationship relationship)
        {
            Assert.IsTrue(relationship.IsManyToOne, "Should be many to one");
        }

        private static void AssertIsOneToOne(IDMRelationship relationship)
        {
            Assert.IsTrue(relationship.IsOneToOne, "Should be one to One");
        }

        private static void AssertIsCompostion(IDMRelationship dmRelationship)
        {
            Assert.IsTrue(dmRelationship.IsCompositionOrAggregation, "Relationship should be composition or aggregation");
        }

        private static void AssertIsAggregation(IDMRelationship dmRelationship)
        {
            Assert.IsTrue(dmRelationship.IsCompositionOrAggregation, "Relationship should be composition or aggregation");
        }
        private static void AssertIsAssociation(IDMRelationship dmRelationship)
        {
            Assert.IsTrue(!dmRelationship.IsCompositionOrAggregation, "Relationship should be association");
        }
        private static IDMRelationship CreateRelationship()
        {
            var relationship = MockRepository.GenerateMock<IDMRelationship>();
            relationship.Stub(dmRelationship => dmRelationship.ReverseRelationship).Return(
                MockRepository.GenerateMock<IDMRelationship>());
            return relationship;
        }

        private static void SetAsAssocation(IDMRelationship dmRelationship)
        {
            if (dmRelationship == null) return;
            dmRelationship.Stub(relationship => relationship.IsAssociation).Return(true);
        }

        private static void SetAsComposition(IDMRelationship dmRelationship)
        {
            if (dmRelationship == null) return;
            dmRelationship.Stub(relationship => relationship.IsCompositionOrAggregation).Return(true);
        }

        private static void SetAsAggregation(IDMRelationship dmRelationship)
        {
            if (dmRelationship == null) return;
            dmRelationship.Stub(relationship => relationship.IsCompositionOrAggregation).Return(true);
        }

        private static void SetAsManyToOne(IDMRelationship dmRelationship)
        {
            dmRelationship.Stub(relationship => relationship.IsManyToOne).Return(true);
            dmRelationship.Stub(relationship => relationship.IsOneToMany).Return(false);
            dmRelationship.Stub(relationship => relationship.IsOneToOne).Return(false);
        }
        private static void SetAsOneToMany(IDMRelationship dmRelationship)
        {
            dmRelationship.Stub(relationship => relationship.IsManyToOne).Return(false);
            dmRelationship.Stub(relationship => relationship.IsOneToMany).Return(true);
            dmRelationship.Stub(relationship => relationship.IsOneToOne).Return(false);
        }

        private static void SetAsOneToOne(IDMRelationship dmRelationship)
        {
            dmRelationship.Stub(relationship => relationship.IsManyToOne).Return(false);
            dmRelationship.Stub(relationship => relationship.IsOneToMany).Return(false);
            dmRelationship.Stub(relationship => relationship.IsOneToOne).Return(true);
        }
        #endregion
    }
}